home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / man / RCS / man.c,v < prev    next >
Encoding:
Text File  |  1991-08-16  |  29.1 KB  |  1,312 lines

  1. head     1.10;
  2. branch   ;
  3. access   ;
  4. symbols  ;
  5. locks    shirriff:1.10; strict;
  6. comment  @ * @;
  7.  
  8.  
  9. 1.10
  10. date     91.08.15.23.13.46;  author ouster;  state Exp;
  11. branches ;
  12. next     1.9;
  13.  
  14. 1.9
  15. date     91.01.08.21.36.35;  author tve;  state Exp;
  16. branches ;
  17. next     1.8;
  18.  
  19. 1.8
  20. date     89.01.20.09.02.45;  author ouster;  state Exp;
  21. branches ;
  22. next     1.7;
  23.  
  24. 1.7
  25. date     89.01.13.17.03.53;  author ouster;  state Exp;
  26. branches ;
  27. next     1.6;
  28.  
  29. 1.6
  30. date     89.01.11.14.28.04;  author ouster;  state Exp;
  31. branches ;
  32. next     1.5;
  33.  
  34. 1.5
  35. date     88.12.29.14.48.45;  author ouster;  state Exp;
  36. branches ;
  37. next     1.4;
  38.  
  39. 1.4
  40. date     88.12.29.10.17.56;  author ouster;  state Exp;
  41. branches ;
  42. next     1.3;
  43.  
  44. 1.3
  45. date     88.12.29.09.40.37;  author ouster;  state Exp;
  46. branches ;
  47. next     1.2;
  48.  
  49. 1.2
  50. date     88.12.28.16.34.38;  author ouster;  state Exp;
  51. branches ;
  52. next     1.1;
  53.  
  54. 1.1
  55. date     88.12.23.17.22.16;  author ouster;  state Exp;
  56. branches ;
  57. next     ;
  58.  
  59.  
  60. desc
  61. @@
  62.  
  63.  
  64. 1.10
  65. log
  66. @Changes to index creation.
  67. (Checked in by shirriff)
  68. @
  69. text
  70. @/* 
  71.  * man.c --
  72.  *
  73.  *    This file contains the "man" program for Sprite.  See the man
  74.  *    page for details on how it works.
  75.  *
  76.  * Copyright 1988 Regents of the University of California
  77.  * Permission to use, copy, modify, and distribute this
  78.  * software and its documentation for any purpose and without
  79.  * fee is hereby granted, provided that the above copyright
  80.  * notice appear in all copies.  The University of California
  81.  * makes no representations about the suitability of this
  82.  * software for any purpose.  It is provided "as is" without
  83.  * express or implied warranty.
  84.  */
  85.  
  86. #ifndef lint
  87. static char rcsid[] = "$Header: /sprite/src/cmds/man/RCS/man.c,v 1.9 91/01/08 21:36:35 tve Exp Locker: ouster $ SPRITE (Berkeley)";
  88. #endif not lint
  89.  
  90. #include <ctype.h>
  91. #include <errno.h>
  92. #include <option.h>
  93. #include <stdio.h>
  94. #include <stdlib.h>
  95. #include <string.h>
  96. #include <sys/types.h>
  97. #include <sys/file.h>
  98. #include <sys/stat.h>
  99.  
  100. /*
  101.  * Name of default configuration file:
  102.  */
  103.  
  104. #ifndef CONFIG_FILE
  105. #define CONFIG_FILE "/sprite/lib/man/config"
  106. #endif
  107.  
  108. /*
  109.  * Information related to command-line options:
  110.  */
  111.  
  112. int typeset = 0;        /* Non-zero means print on typesetter
  113.                  * instead of on terminal. */
  114. int noMore = 0;            /* Non-zero means don't filter output through
  115.                  * the "more" program. */
  116. char *sectionName = NULL;    /* Name of section to search in. */
  117. char *configFile = CONFIG_FILE;    /* Configuration file that describes where
  118.                  * man pages are located. */
  119. int reformat = 0;        /* Non-zero means reformat man page even if
  120.                  * formatted copy appears to be up-to-date. */
  121. int makeIndex = 0;        /* Non-zero means generate index from args
  122.                  * rather than printing man pages. */
  123. int keywordLookup = 0;        /* Non-zero means "man -k": look for
  124.                  * keywords. */
  125. int where = 0;            /* Non-zero means say where man page is. */
  126.  
  127. Option optionArray[] = {
  128.     {OPT_STRING, "c", (char *) &configFile,
  129.         "Name of configuration file (default: /sprite/lib/man/config)"},
  130.     {OPT_TRUE, "f", (char *) &keywordLookup,
  131.         "Identical to \"-k\" (provided for UNIX compatibility)"},
  132.     {OPT_TRUE, "i", (char *) &makeIndex,
  133.         "Generate index from file name arguments"},
  134.     {OPT_TRUE, "k", (char *) &keywordLookup,
  135.         "Print index information for keyword arguments"},
  136.     {OPT_TRUE, "r", (char *) &reformat,
  137.         "Force man page to be reformatted, even if up-to-date"},
  138.     {OPT_STRING, "s", (char *) §ionName,
  139.         "Section name in which to search for man page(s)"},
  140.     {OPT_TRUE, "t", (char *) &typeset,
  141.         "Print man page on typesetter instead of on terminal"},
  142.     {OPT_TRUE, "w", (char *) &where,
  143.         "Print where the man page was found"},
  144.     {OPT_TRUE, "", (char *) &noMore,
  145.         "Don't filter output through \"more\" program"},
  146. };
  147.  
  148. /*
  149.  * One of the data structures built up by this program is the one
  150.  * that describes the directories containing man page sources, and
  151.  * the corresponding directories containing pre-formatted man pages.
  152.  */
  153.  
  154. typedef struct {
  155.     char *sourceDir;        /* Directory holding man page sources. */
  156.     char *fmtDir;        /* Directory holding formatted entries,
  157.                  * by same name. */
  158.     char *sectionName;        /* Preferred name for this section of the
  159.                  * manual. */
  160. } ManDir;
  161.  
  162. #define MAX_DIRS 100
  163. ManDir dirs[MAX_DIRS];
  164. int numDirs;            /* Number of valid entries in dirs. */
  165.  
  166. /*
  167.  * The data structure below is used to hold all the index information
  168.  * associated with a manual entry.
  169.  */
  170.  
  171. #define MAX_NAMES 100
  172. #define MAX_KEYWORDS 100
  173. #define NAME_CHARS 1000
  174. #define KEYWORD_CHARS 1000
  175. #define SYNOPSIS_CHARS 100
  176. #define FILE_CHARS 100
  177.  
  178. typedef struct {
  179.     char fileName[FILE_CHARS];        /* Base name of file containing man
  180.                      * page (everything up to "."). */
  181.     char *names[MAX_NAMES+1];        /* Names of procedures or programs
  182.                      * described by this entry.  These
  183.                      * fields come from the "NAME" manual
  184.                      * section.  Terminated by a NULL
  185.                      * pointer. */
  186.     char synopsis[SYNOPSIS_CHARS];    /* Short description of the entry.
  187.                      * Comes from the part of the "NAME"
  188.                      * section that follows the dash. */
  189.     char *keywords[MAX_KEYWORDS+1];    /* Keywords associated with this
  190.                      * manual entry.  Comes from the
  191.                      * "KEYWORD" section of the entry, if
  192.                      * there is one.  Terminated by a
  193.                      * NULL pointer. */
  194.     char nameBuffer[NAME_CHARS];    /* Storage space for names. */
  195.     char keywordBuffer[KEYWORD_CHARS];    /* Storage space for keywords. */
  196. } IndexEntry;
  197.  
  198. /*
  199.  * Commands to use for formatting and printing manual pages.  The %s's
  200.  * in these commands get filled in with particular file names in the
  201.  * code below.
  202.  */
  203.  
  204. #define FORMAT            "nroff -man -Tcrt %s > %s"
  205. #define FORMAT_PRINT        "nroff -man -Tcrt %s | %s -s"
  206. #define FORMAT_PRINT_NO_MORE    "nroff -man -Tcrt %s"
  207. #define PRINT            "%s -s %s"
  208. #define PRINT_NO_MORE        "cat %s"
  209. #define TYPESET            "ditroff -man %s"
  210.  
  211. /*
  212.  *----------------------------------------------------------------------
  213.  *
  214.  * NextLine --
  215.  *
  216.  *    Read the next line of a given file, skipping comment lines.
  217.  *    Break the line up into fields separated by white space.
  218.  *
  219.  * Results:
  220.  *    The return value is the number of fields in the line (i.e. the
  221.  *    number of elements of argv that are now valid).  If EOF was
  222.  *    encountered, then the return value is -1.  The fields pointed
  223.  *    to by argv are allocated in static storage, so they'll only
  224.  *    be valid up until the next call to this procedure.
  225.  *
  226.  * Side effects:
  227.  *    The argv array is modified.  If the line contains more fields than
  228.  *    are permitted by maxArgs, then an error message is output on stderr
  229.  *    and the extra fields are ignored.
  230.  *
  231.  *----------------------------------------------------------------------
  232.  */
  233.  
  234. int
  235. NextLine(file, maxArgs, argv)
  236.     FILE *file;            /* File from which to read. */
  237.     int maxArgs;        /* Number of entries in argv. */
  238.     char **argv;        /* Array to fill in with pointers to the
  239.                  * fields of the line. */
  240. {
  241. #define MAX_CHARS 200
  242.     static char buffer[MAX_CHARS];
  243.     register char *p;
  244.     int i;
  245.  
  246.     while (1) {
  247.     if (fgets(buffer, MAX_CHARS, file) == NULL) {
  248.         return -1;
  249.     }
  250.     for (p = buffer; ; p++) {
  251.         if (isspace(*p)) {
  252.         continue;
  253.         }
  254.         if ((*p != '#') && (*p != 0)) {
  255.         goto gotLine;
  256.         }
  257.         break;
  258.     }
  259.     }
  260.  
  261.     /*
  262.      * Break the line up into fields.
  263.      */
  264.  
  265.     gotLine:
  266.     for (i = 0, p = buffer; ; i++) {
  267.     while (isspace(*p)) {
  268.         p++;
  269.     }
  270.     if (*p == 0) {
  271.         return i;
  272.     }
  273.     if (i >= maxArgs) {
  274.         break;
  275.     }
  276.     argv[i] = p;
  277.     while (!isspace(*p)) {
  278.         p++;
  279.     }
  280.     *p = 0;
  281.     p++;
  282.     }
  283.  
  284.     /*
  285.      * Ran out of space to store field info.
  286.      */
  287.  
  288.     fprintf(stderr,
  289.         "More than %d fields in config file line;  extras ignored.\n",
  290.         maxArgs);
  291.     return maxArgs;
  292. }
  293.  
  294. /*
  295.  *----------------------------------------------------------------------
  296.  *
  297.  * ReadConfig --
  298.  *
  299.  *    This procedure reads in the configuration file given by
  300.  *    name, and builds a list of all the directories that match
  301.  *    the given section.
  302.  *
  303.  * Results:
  304.  *    Zero is returned if all went well, -1 if there was an error.
  305.  *
  306.  * Side effects:
  307.  *    The dirs data structure is created.  If an error occurred, then
  308.  *    an error message is printed.
  309.  *
  310.  *----------------------------------------------------------------------
  311.  */
  312.  
  313. int
  314. ReadConfig(name, section)
  315.     char *name;            /* Name of config file. */
  316.     char *section;        /* Only consider directories that match
  317.                  * this string;  if NULL, consider
  318.                  * everything. */
  319. {
  320. #define MAX_FIELDS 50
  321.     char *argv[MAX_FIELDS];
  322.     FILE *f;
  323.     int j, argc;
  324.  
  325.     f = fopen(name, "r");
  326.     if (f == NULL) {
  327.     fprintf(stderr, "Couldn't open \"%s\": %s.\n", name, strerror(errno));
  328.     return -1;
  329.     }
  330.  
  331.     for (numDirs = 0; numDirs < MAX_DIRS; ) {
  332.     argc = NextLine(f, MAX_FIELDS, argv);
  333.     if (argc < 0) {
  334.         fclose(f);
  335.         return 0;
  336.     }
  337.     for (j = 2; j < argc; j++) {
  338.         if ((section == NULL) || (strcmp(argv[j], section) == 0)) {
  339.         goto makeEntry;
  340.         }
  341.     }
  342.     continue;
  343.  
  344.     /*
  345.      * This line matched the section name;  add an entry to dirs.
  346.      */
  347.  
  348.     makeEntry:
  349.     dirs[numDirs].sourceDir =
  350.         (char *) malloc((unsigned) (strlen(argv[0]) + 1));
  351.     strcpy(dirs[numDirs].sourceDir, argv[0]);
  352.     dirs[numDirs].fmtDir =
  353.         (char *) malloc((unsigned) (strlen(argv[1]) + 1));
  354.     strcpy(dirs[numDirs].fmtDir, argv[1]);
  355.     dirs[numDirs].sectionName =
  356.         (char *) malloc((unsigned) (strlen(argv[2]) + 1));
  357.     strcpy(dirs[numDirs].sectionName, argv[2]);
  358.     numDirs++;
  359.     }
  360.  
  361.     fprintf(stderr, "Too many lines in \"%s\": ignoring extras.\n", name);
  362.     return 0;
  363. }
  364.  
  365. /*
  366.  *----------------------------------------------------------------------
  367.  *
  368.  * FindSection --
  369.  *
  370.  *    Advance a file just past the header line for a given section.
  371.  *
  372.  * Results:
  373.  *    Returns 0 if the given section was found, -1 if EOF was reached
  374.  *    before the desired section.
  375.  *
  376.  * Side effects:
  377.  *    Characters are read from file until a line is found in the form
  378.  *    ".SH section".  The line and its terminating newline are read and
  379.  *    discarded.
  380.  *
  381.  *----------------------------------------------------------------------
  382.  */
  383.  
  384. int
  385. FindSection(file, section)
  386.     FILE *file;            /* File to read. */
  387.     char *section;        /* Section to search for. */
  388. {
  389. #define LINE_LENGTH 200
  390.     char line[LINE_LENGTH];
  391.     register char *p1, *p2;
  392.  
  393.     while (fgets(line, LINE_LENGTH, file) != NULL) {
  394.     if ((line[0] != '.') || (line[1] != 'S') || (line[2] != 'H')) {
  395.         continue;
  396.     }
  397.     for (p1 = &line[3]; isspace(*p1); p1++) {
  398.         /* Skip white space. */
  399.     }
  400.     for (p2 = section; ; p1++, p2++) {
  401.         if ((*p2 == 0) && ((*p1 == 0) || (isspace(*p1)))) {
  402.         return 0;
  403.         }
  404.         if (*p2 != *p1) {
  405.         break;
  406.         }
  407.     }
  408.     }
  409.     return -1;
  410. }
  411.  
  412. /*
  413.  *----------------------------------------------------------------------
  414.  *
  415.  * IndexFromMan --
  416.  *
  417.  *    Read the source file for a manual entry and generate an
  418.  *    index entry for it.
  419.  *
  420.  * Results:
  421.  *    Normally zero is returned, but if an error occurs then a
  422.  *    no-zero value is returned.  *indexPtr is filled in with
  423.  *    information describing this man page.
  424.  *
  425.  * Side effects:
  426.  *    If an error occurs in reading the entry, then an error
  427.  *    message gets printed.
  428.  *
  429.  *----------------------------------------------------------------------
  430.  */
  431.  
  432. int
  433. IndexFromMan(fileName, indexPtr)
  434.     char *fileName;            /* Name of file containing man page. */
  435.     register IndexEntry *indexPtr;    /* Pointer to index entry. */
  436. {
  437.     register FILE *file;
  438.     register char *p;
  439.     register int c;
  440.     char *limit;
  441.     int index;
  442.  
  443.     file = fopen(fileName, "r");
  444.     if (file == NULL) {
  445.     fprintf(stderr, "Couldn't open \"%s\": %s.\n", fileName,
  446.         strerror(errno));
  447.     return -1;
  448.     }
  449.  
  450.     /*
  451.      * Parse off the root of the file name.
  452.      */
  453.  
  454.     for (p = fileName; (*p != '.') && (*p != 0); p++) {
  455.     /* Null loop body. */
  456.     }
  457.     if ((p-fileName) > FILE_CHARS) {
  458.     fprintf(stderr, "File name \"%s\" too long.\n", fileName);
  459.     goto error;
  460.     }
  461.     strncpy(indexPtr->fileName, fileName, (p-fileName));
  462.     indexPtr->fileName[p-fileName] = 0;
  463.  
  464.     /*
  465.      * Parse off the names (a bunch of strings, all but the last of which
  466.      * are terminated by commas, with the last terminated by space).
  467.      */
  468.  
  469.     if (FindSection(file, "NAME") != 0) {
  470.     fprintf(stderr, "Couldn't find \"NAME\" section in \"%s\".\n",
  471.         fileName);
  472.     goto error;
  473.     }
  474.     c = getc(file);
  475.  
  476.     /*
  477.      * Skip any troff commands at the beginning of the section.
  478.      */
  479.  
  480.     while (c == '.') {
  481.     while ((c != '\n') && (c != EOF)) {
  482.         c = getc(file);
  483.     }
  484.     c = getc(file);
  485.     }
  486.  
  487.     /*
  488.      * Parse off the procedure names.
  489.      */
  490.  
  491.     p = indexPtr->nameBuffer;
  492.     limit = &indexPtr->nameBuffer[NAME_CHARS-1];
  493.     for (index = 0; index < MAX_NAMES; ) {
  494.     while (isspace(c)) {
  495.         c = getc(file);
  496.     }
  497.     indexPtr->names[index] = p;
  498.     while (!isspace(c) && (c != ',') && (p < limit)) {
  499.         *p = c;
  500.         p++;
  501.         c = getc(file);
  502.     }
  503.     if (p >= limit) {
  504.         break;
  505.     }
  506.     if (indexPtr->names[index] != p) {
  507.         *p = 0;
  508.         p++;
  509.         index++;
  510.     }
  511.     if (c != ',') {
  512.         break;
  513.     }
  514.     c = getc(file);
  515.     }
  516.     if (c == EOF) {
  517.     fprintf(stderr, "Unexpected end-of-file in NAME section of \"%s\".\n",
  518.         fileName);
  519.     goto error;
  520.     }
  521.     if ((index == MAX_NAMES) || (p >= limit)) {
  522.     fprintf(stderr, "Too many names in \"%s\";  skipped the extras.\n",
  523.         fileName);
  524.     }
  525.     indexPtr->names[index] = 0;
  526.  
  527.     /*
  528.      * Skip up to and through a hyphen and any following space, then
  529.      * use the rest of the line as a synopsis.
  530.      */
  531.  
  532.     while ((c != '-') && (c != EOF)) {
  533.     c = getc(file);
  534.     }
  535.     for (c = getc(file); isspace(c); c = getc(file)) {
  536.     /* Null loop body. */
  537.     }
  538.     ungetc(c, file);
  539.     fgets(indexPtr->synopsis, SYNOPSIS_CHARS, file);
  540.     for (p = indexPtr->synopsis; *p != 0; p++) {
  541.     if (*p == '\n') {
  542.         *p = 0;
  543.         break;
  544.     }
  545.     }
  546.  
  547.     /*
  548.      * Skip to the keywords section and parse off the keywords in a
  549.      * fashion similar to the names, except that (a) it's OK not to have
  550.      * a KEYWORDS section, (b) it's OK to have space in a keyword, and
  551.      * (c) the last keyword is terminated by newline.
  552.      */
  553.  
  554.     if (FindSection(file, "KEYWORDS") != 0) {
  555.     indexPtr->keywords[0] = NULL;
  556.     goto done;
  557.     }
  558.     c = getc(file);
  559.     p = indexPtr->keywordBuffer;
  560.     limit = &indexPtr->keywordBuffer[KEYWORD_CHARS-1];
  561.     for (index = 0; index < MAX_KEYWORDS; ) {
  562.     while (isspace(c)) {
  563.         c = getc(file);
  564.     }
  565.     indexPtr->keywords[index] = p;
  566.     while ((c != ',') && (c != '\n') && (c != EOF) && (p < limit)) {
  567.         *p = c;
  568.         p++;
  569.         c = getc(file);
  570.     }
  571.     if (p >= limit) {
  572.         break;
  573.     }
  574.     if (indexPtr->keywords[index] != p) {
  575.         *p = 0;
  576.         p++;
  577.         index++;
  578.     }
  579.     if (c == EOF) {
  580.         fprintf(stderr,
  581.             "Unexpected end-of-file in KEYWORDS section of \"%s\".\n",
  582.             fileName);
  583.         goto error;
  584.     }
  585.     if (c == '\n') {
  586.         break;
  587.     }
  588.     c = getc(file);
  589.     }
  590.     if ((index == MAX_NAMES) || (p >= limit)) {
  591.     fprintf(stderr, "Too many names in \"%s\";  skipped the extras.\n",
  592.         fileName);
  593.     }
  594.     indexPtr->keywords[index] = 0;
  595.  
  596.     done:
  597.     fclose(file);
  598.     return 0;
  599.  
  600.     error:
  601.     fclose(file);
  602.     return -1;
  603. }
  604.  
  605. /*
  606.  *----------------------------------------------------------------------
  607.  *
  608.  * PrintIndex --
  609.  *
  610.  *    Read manual pages and print index entries on standard output.
  611.  *
  612.  * Results:
  613.  *    None.
  614.  *
  615.  * Side effects:
  616.  *    Information gets printed on standard output.  Error messages
  617.  *    may appear on stderr.
  618.  *
  619.  *----------------------------------------------------------------------
  620.  */
  621.  
  622. void
  623. PrintIndex(argc, argv)
  624.     int argc;            /* Number of files to read. */
  625.     char **argv;        /* Names of files to read. */
  626. {
  627.     IndexEntry index;
  628.     int i;
  629.     char **p;
  630.  
  631.     for (i = 0; i < argc; i++) {
  632.     if (IndexFromMan(argv[i], &index) != 0) {
  633.         continue;
  634.     }
  635.     printf("%s\n", index.fileName);
  636.     for (p = index.names; *p != 0; p++) {
  637.         printf("%s, ", *p);
  638.     }
  639.     putchar('\n');
  640.     printf("%s\n", index.synopsis);
  641.     for (p = index.keywords; *p != 0; p++) {
  642.         printf("%s, ", *p);
  643.     }
  644.     putchar('\n');
  645.     }
  646. }
  647.  
  648. /*
  649.  *----------------------------------------------------------------------
  650.  *
  651.  * ReadNextIndex --
  652.  *
  653.  *    Given a handle for an index file created by PrintIndex, read
  654.  *    the next index entry from the file.
  655.  *
  656.  * Results:
  657.  *    Zero is returned if all went well;  otherwise -1 is returned
  658.  *    and an error message is printed on stderr.  The fields in
  659.  *    *indexPtr will be filled in with information describing this
  660.  *    index entry.
  661.  *
  662.  * Side effects:
  663.  *    The position in file is advanced.
  664.  *
  665.  *----------------------------------------------------------------------
  666.  */
  667.  
  668. int
  669. ReadNextIndex(file, indexPtr)
  670.     FILE *file;            /* File to read. */
  671.     IndexEntry *indexPtr;    /* Entry to fill in. */
  672. {
  673.     register char *p;
  674.     int i;
  675.  
  676.     /*
  677.      * Read in the file name line.
  678.      */
  679.  
  680.     if (fgets(indexPtr->fileName, FILE_CHARS, file) == NULL) {
  681.     return -1;
  682.     }
  683.     for (p = indexPtr->fileName; *p != '\n'; p++) {
  684.     if (*p == 0) {
  685.         fprintf(stderr, "Filename line for \"%s\" too long.\n",
  686.             indexPtr->fileName);
  687.         return -1;
  688.     }
  689.     }
  690.     *p = 0;
  691.  
  692.     /*
  693.      * Read in and parse the keyword line.
  694.      */
  695.  
  696.     if (fgets(indexPtr->nameBuffer, NAME_CHARS, file) == NULL) {
  697.     fprintf(stderr, "End-of-file in name line for \"%s\".\n",
  698.         indexPtr->fileName);
  699.     return -1;
  700.     }
  701.     for (i = 0, p = indexPtr->nameBuffer; i < MAX_NAMES; ) {
  702.     while (isspace(*p)) {
  703.         p++;
  704.     }
  705.     indexPtr->names[i] = p;
  706.     while ((*p != ',') && (*p != 0)) {
  707.         p++;
  708.     }
  709.     if (p != indexPtr->names[i]) {
  710.         i++;
  711.     }
  712.     if (*p == 0) {
  713.         break;
  714.     }
  715.     *p = 0;
  716.     p++;
  717.     }
  718.     indexPtr->names[i] = 0;
  719.     if ((p[-1] != '\n') || (i == MAX_NAMES)) {
  720.     fprintf(stderr, "Too many names for \"%s\".\n", indexPtr->fileName);
  721.     return -1;
  722.     }
  723.  
  724.     /*
  725.      * Read in the synopsis line;  there's no parsing to do.
  726.      */
  727.  
  728.     if (fgets(indexPtr->synopsis, SYNOPSIS_CHARS, file) == NULL) {
  729.     fprintf(stderr, "End-of-file in synopsis line for \"%s\".\n",
  730.         indexPtr->fileName);
  731.     return -1;
  732.     }
  733.     for (p = indexPtr->synopsis; *p != '\n'; p++) {
  734.     if (*p == 0) {
  735.         fprintf(stderr, "Synopsis line for \"%s\" too long.\n",
  736.             indexPtr->fileName);
  737.         return -1;
  738.     }
  739.     }
  740.     *p = 0;
  741.  
  742.     /*
  743.      * Read in and parse the keywords line.
  744.      */
  745.  
  746.     if (fgets(indexPtr->keywordBuffer, KEYWORD_CHARS, file) == NULL) {
  747.     fprintf(stderr, "End-of-file in keywords line for \"%s\".\n",
  748.         indexPtr->fileName);
  749.     return -1;
  750.     }
  751.     for (i = 0, p = indexPtr->keywordBuffer; i < MAX_KEYWORDS;) {
  752.     while (isspace(*p)) {
  753.         p++;
  754.     }
  755.     indexPtr->keywords[i] = p;
  756.     while ((*p != ',') && (*p != 0)) {
  757.         p++;
  758.     }
  759.     if (p != indexPtr->keywords[i]) {
  760.         i++;
  761.     }
  762.     if (*p == 0) {
  763.         break;
  764.     }
  765.     *p = 0;
  766.     p++;
  767.     }
  768.     indexPtr->keywords[i] = 0;
  769.     if ((p[-1] != '\n') || (i == MAX_KEYWORDS)) {
  770.     fprintf(stderr, "Too many keywords for \"%s\".\n", indexPtr->fileName);
  771.     return -1;
  772.     }
  773.     return 0;
  774. }
  775.  
  776. /*
  777.  *----------------------------------------------------------------------
  778.  *
  779.  * PrettyPrintIndex --
  780.  *
  781.  *    Print an index entry in human-readable form on standard output.
  782.  *
  783.  * Results:
  784.  *    None.
  785.  *
  786.  * Side effects:
  787.  *    None.
  788.  *
  789.  *----------------------------------------------------------------------
  790.  */
  791.  
  792. void
  793. PrettyPrintIndex(indexPtr, section)
  794.     IndexEntry *indexPtr;    /* Entry to print. */
  795.     char *section;        /* Name to use for manual section. */
  796. {
  797. #define COLS_FOR_NAMES 24
  798.     int i, numCols;
  799.  
  800.     numCols = 0;
  801.     for (i = 0; indexPtr->names[i] != 0; i++) {
  802.     if (i != 0) {
  803.         fputs(", ", stdout);
  804.         numCols += 2;
  805.     }
  806.     fputs(indexPtr->names[i], stdout);
  807.     numCols += strlen(indexPtr->names[i]);
  808.     }
  809.     numCols += printf(" (%s) ", section);
  810.     if (numCols < COLS_FOR_NAMES) {
  811.     printf("%*c", COLS_FOR_NAMES-numCols, ' ');
  812.     }
  813.     printf("- %s\n", indexPtr->synopsis);
  814. }
  815.  
  816. /*
  817.  *----------------------------------------------------------------------
  818.  *
  819.  * PrintByKeyword --
  820.  *
  821.  *    Given a keyword, locate the index entries (if any) corresponding
  822.  *    to that keyword and print out each matching index entry.
  823.  *
  824.  * Results:
  825.  *    Information is printed for each index entry that contains
  826.  *    the keyword as a substring of the entry's name, synopsis, or
  827.  *    keyword fields.  If no matching entry was found, then an
  828.  *    error message is printed.
  829.  *
  830.  * Side effects:
  831.  *    None.
  832.  *
  833.  *----------------------------------------------------------------------
  834.  */
  835.  
  836. int
  837. PrintByKeyword(keyword)
  838.     char *keyword;            /* String to search for. */
  839. {
  840.     int i, foundMatch;
  841.     FILE *f;
  842.     char **p;
  843.     char indexName[400];
  844.     IndexEntry index;
  845.  
  846.     foundMatch = 0;
  847.     for (i = 0; i < numDirs; i++) {
  848.     sprintf(indexName, "%.350s/index", dirs[i].sourceDir);
  849.     f = fopen(indexName, "r");
  850.     if (f == NULL) {
  851.         continue;
  852.     }
  853.     while (ReadNextIndex(f, &index) == 0) {
  854.         for (p = index.names; *p != 0; p++) {
  855.         if (strstr(*p, keyword) != 0) {
  856.             goto found;
  857.         }
  858.         }
  859.         if (strstr(index.synopsis, keyword) != 0) {
  860.         goto found;
  861.         }
  862.         for (p = index.keywords; *p != 0; p++) {
  863.         if (strstr(*p, keyword) != 0) {
  864.             goto found;
  865.         }
  866.         }
  867.         continue;
  868.  
  869.         found:
  870.         PrettyPrintIndex(&index, dirs[i].sectionName);
  871.         foundMatch = 1;
  872.     }
  873.     fclose(f);
  874.     }
  875.     if (!foundMatch) {
  876.     fprintf(stderr,
  877.         "Couldn't find any manual entries related to \"%s\".\n",
  878.         keyword);
  879.     }
  880. }
  881.  
  882. /*
  883.  *----------------------------------------------------------------------
  884.  *
  885.  * main --
  886.  *
  887.  *    This is the main program, which runs the whole show.
  888.  *
  889.  * Results:
  890.  *    None.
  891.  *
  892.  * Side effects:
  893.  *    Read the man page for details.
  894.  *
  895.  *----------------------------------------------------------------------
  896.  */
  897.  
  898. main(argc, argv)
  899.     int argc;
  900.     char **argv;
  901. {
  902.     int i, result;
  903.     char srcName[500], fmtName[500];
  904.     char command[1100];
  905.     char *progName = argv[0];
  906.     char *pager;
  907.     struct stat srcStat, fmtStat;
  908.     IndexEntry index;
  909.  
  910.     pager = getenv("PAGER");
  911.     if(pager == 0) pager = "more";
  912.  
  913.     /*
  914.      * Process command-line options and figure out what section to
  915.      * look in.
  916.      */
  917.  
  918.     result = 0;
  919.     argc = Opt_Parse(argc, argv, optionArray, Opt_Number(optionArray), 0);
  920.     if ((sectionName == NULL) && (argc > 1) && (isdigit(argv[1][0]))) {
  921.     sectionName = argv[1];
  922.     argc--;
  923.     argv++;
  924.     }
  925.  
  926.     /*
  927.      * If we're just generating an index, do it here and quit.
  928.      */
  929.  
  930.     if (makeIndex) {
  931.     PrintIndex(argc-1, &argv[1]);
  932.     exit(0);
  933.     }
  934.  
  935.     /*
  936.      * Read in the configuration file, and make sure that there is at
  937.      * least one place to look for the desired manual page.
  938.      */
  939.  
  940.     if (ReadConfig(configFile, sectionName) != 0) {
  941.     exit(1);
  942.     }
  943.     if (numDirs == 0) {
  944.     if (sectionName != NULL) {
  945.         fprintf(stderr, "No manual section named \"%s\".\n", sectionName);
  946.     } else {
  947.         fprintf(stderr, "The config file (%s) is empty!\n", CONFIG_FILE);
  948.     }
  949.     exit(1);
  950.     }
  951.  
  952.     if (argc == 1) {
  953.     fprintf(stderr, "Usage:  %s [options] entryName entryName ...\n",
  954.         progName);
  955.     exit(1);
  956.     }
  957.  
  958.     /*
  959.      * Loop over all of the named man pages, processing each one separately.
  960.      */
  961.  
  962.     for (argv++ ; argc > 1; argc--, argv++) {
  963.     /*
  964.      * Handle special case of keyword lookup.
  965.      */
  966.  
  967.     if (keywordLookup) {
  968.         PrintByKeyword(argv[0]);
  969.         continue;
  970.     }
  971.  
  972.     /*
  973.      * Search for the desired entry in two passes.  In the first pass,
  974.      * look in each of the available directories for a file named
  975.      * "foo.man" where foo is the entry's name.  If this doesn't work,
  976.      * then the second pass reads the index files in each of the
  977.      * available directories, checking for an entry with the desired
  978.      * name.
  979.      */
  980.     
  981.     for (i = 0; i < numDirs; i++) {
  982.         sprintf(srcName, "%.350s/%.100s.man", dirs[i].sourceDir, argv[0]);
  983.         if (access(srcName, R_OK) == 0) {
  984.         sprintf(fmtName, "%.350s/%.100s.man", dirs[i].fmtDir, argv[0]);
  985.         goto gotEntry;
  986.         }
  987.     }
  988.     for (i = 0; i < numDirs; i++) {
  989.         FILE *f;
  990.         char **p;
  991.  
  992.         sprintf(srcName, "%.350s/index", dirs[i].sourceDir);
  993.         f = fopen(srcName, "r");
  994.         if (f == NULL) {
  995.         continue;
  996.         }
  997.         while (ReadNextIndex(f, &index) == 0) {
  998.         for (p = index.names; *p != 0; p++) {
  999.             if (strcmp(*p, argv[0]) == 0) {
  1000.             sprintf(srcName, "%.350s/%.100s.man",
  1001.                 dirs[i].sourceDir, index.fileName);
  1002.             if (access(srcName, R_OK) == 0) {
  1003.                 sprintf(fmtName, "%.350s/%.100s.man",
  1004.                     dirs[i].fmtDir, index.fileName);
  1005.                 fclose(f);
  1006.                 goto gotEntry;
  1007.             }
  1008.             }
  1009.         }
  1010.         }
  1011.         fclose(f);
  1012.     }
  1013.  
  1014.     /*
  1015.      * Couldn't find the manual entry.
  1016.      */
  1017.  
  1018.     if (sectionName == NULL) {
  1019.         fprintf(stderr, "No manual entry for \"%s\".\n", argv[0]);
  1020.     } else {
  1021.         fprintf(stderr,
  1022.             "No manual entry for \"%s\" in section \"%s\".\n",
  1023.             argv[0], sectionName);
  1024.     }
  1025.     result = 1;
  1026.     continue;
  1027.  
  1028.     /*
  1029.      * If the entry is to be typeset, just do it.
  1030.      */
  1031.  
  1032.     gotEntry:
  1033.     if (typeset) {
  1034.         sprintf(command, TYPESET, srcName);
  1035.         if (system(command) != 0) {
  1036.         printf("Error in typesetting \"%s\" man page.\n", argv[0]);
  1037.         result = 1;
  1038.         }
  1039.         continue;
  1040.     }
  1041.  
  1042.     /*
  1043.      * See if there is an up-to-date formatted version of the page.
  1044.      * If not, then regenerate it.  (If the formatted directory is
  1045.      * "-" that means no formatted version of the man page is to be
  1046.      * kept)
  1047.      */
  1048.  
  1049.     if (strcmp(dirs[i].fmtDir, "-") == 0) {
  1050.         goto couldntFormat;
  1051.     }
  1052.     if ((stat(srcName, &srcStat) != 0) || (stat(fmtName, &fmtStat) != 0)
  1053.         || (srcStat.st_mtime > fmtStat.st_mtime) || reformat) {
  1054.         printf("Reformatting manual entry.  Please wait...\n");
  1055.         sprintf(command, FORMAT, srcName, fmtName);
  1056.         if (system(command) != 0) {
  1057.         unlink(fmtName);
  1058.         goto couldntFormat;
  1059.         }
  1060.  
  1061.         /*
  1062.          * Reprotect the formatted file so that anyone can overwrite
  1063.          * it later.
  1064.          */
  1065.  
  1066.         chmod(fmtName, 0666);
  1067.     }
  1068.  
  1069.     /*
  1070.      * Print the formatted version of the man page.
  1071.      */
  1072.  
  1073.     if (where) {
  1074.         printf("Man page found in %s\n", fmtName);
  1075.         continue;
  1076.     }
  1077.     if (noMore) {
  1078.         sprintf(command, PRINT_NO_MORE, fmtName);
  1079.     } else {
  1080.         sprintf(command, PRINT, pager, fmtName);
  1081.     }
  1082.     if (system(command) == 0) {
  1083.         continue;
  1084.     }
  1085.  
  1086.     /*
  1087.      * We get here if it wasn't possible to format and/or print the
  1088.      * man page.  Try one last desperation move:  print and format
  1089.      * in a single command.
  1090.      */
  1091.  
  1092.     couldntFormat:
  1093.     if (noMore) {
  1094.         sprintf(command, FORMAT_PRINT_NO_MORE, srcName);
  1095.     } else {
  1096.         sprintf(command, FORMAT_PRINT, srcName, pager);
  1097.     }
  1098.     (void) system(command);
  1099.     }
  1100.     exit(result);
  1101. }
  1102. @
  1103.  
  1104.  
  1105. 1.9
  1106. log
  1107. @Added recognition of PAGER environment variable instead of stupidly
  1108. calling more
  1109. @
  1110. text
  1111. @d18 1
  1112. a18 1
  1113. static char rcsid[] = "$Header: /sprite/src/cmds/man/RCS/man.c,v 1.9 91/01/08 21:26:28 tve Exp $ SPRITE (Berkeley)";
  1114. d332 1
  1115. a332 1
  1116.         if ((*p2 == 0) && ((*p1 == 0) || (*p1 == '\n'))) {
  1117. d406 16
  1118. @
  1119.  
  1120.  
  1121. 1.8
  1122. log
  1123. @Ignore errors when formatting and printing at the same time.
  1124. @
  1125. text
  1126. @d18 1
  1127. a18 1
  1128. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.7 89/01/13 17:03:53 ouster Exp $ SPRITE (Berkeley)";
  1129. d56 1
  1130. d73 2
  1131. d136 1
  1132. a136 1
  1133. #define FORMAT_PRINT        "nroff -man -Tcrt %s | more -s"
  1134. d138 1
  1135. a138 1
  1136. #define PRINT            "more -s %s"
  1137. d821 1
  1138. d825 3
  1139. d988 4
  1140. d995 1
  1141. a995 1
  1142.         sprintf(command, PRINT, fmtName);
  1143. d1011 1
  1144. a1011 1
  1145.         sprintf(command, FORMAT_PRINT, srcName);
  1146. @
  1147.  
  1148.  
  1149. 1.7
  1150. log
  1151. @Don't print keywords in "-k" listings.
  1152. @
  1153. text
  1154. @d18 1
  1155. a18 1
  1156. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.6 89/01/11 14:28:04 ouster Exp Locker: ouster $ SPRITE (Berkeley)";
  1157. d1002 1
  1158. a1002 5
  1159.     if (system(command) == 0) {
  1160.         continue;
  1161.     }
  1162.     result = 1;
  1163.     printf("Couldn't find a way to print \"%s\" man page.\n", argv[0]);
  1164. @
  1165.  
  1166.  
  1167. 1.6
  1168. log
  1169. @Allow "-" formatted directory to not keep formatted versions.
  1170. @
  1171. text
  1172. @d18 1
  1173. a18 1
  1174. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.5 88/12/29 14:48:45 ouster Exp $ SPRITE (Berkeley)";
  1175. d25 1
  1176. d52 4
  1177. d60 6
  1178. d86 2
  1179. d95 32
  1180. d251 1
  1181. a251 1
  1182.     int i, j, argc;
  1183. d283 3
  1184. d296 501
  1185. d819 1
  1186. d835 9
  1187. d871 3
  1188. d875 5
  1189. d881 6
  1190. a886 2
  1191.      * Scan through all of the matching sections, looking for the source
  1192.      * for the desired page.
  1193. d892 2
  1194. a893 1
  1195.         break;
  1196. d896 8
  1197. a903 7
  1198.     if (i >= numDirs) {
  1199.         if (sectionName == NULL) {
  1200.         fprintf(stderr, "No manual entry for \"%s\".\n", argv[0]);
  1201.         } else {
  1202.         fprintf(stderr,
  1203.             "No manual entry for \"%s\" in section \"%s\".\n",
  1204.             argv[0], sectionName);
  1205. d905 15
  1206. a919 2
  1207.         result = 1;
  1208.         continue;
  1209. d923 1
  1210. a923 1
  1211.      * If the page is to be typeset, just do it.
  1212. d926 15
  1213. a959 1
  1214.     sprintf(fmtName, "%.350s/%.100s.man", dirs[i].fmtDir, argv[0]);
  1215. @
  1216.  
  1217.  
  1218. 1.5
  1219. log
  1220. @Retain ".man" suffix on formatted man pages.
  1221. @
  1222. text
  1223. @d18 1
  1224. a18 1
  1225. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.4 88/12/29 10:17:56 ouster Exp Locker: ouster $ SPRITE (Berkeley)";
  1226. d337 1
  1227. a337 1
  1228.      * If the page is to be typset, just do it.
  1229. d351 3
  1230. a353 1
  1231.      * If not, then regenerate it.
  1232. d356 3
  1233. @
  1234.  
  1235.  
  1236. 1.4
  1237. log
  1238. @Don't put execute permission on formatted files.
  1239. @
  1240. text
  1241. @d18 1
  1242. a18 1
  1243. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.3 88/12/29 09:40:37 ouster Exp Locker: ouster $ SPRITE (Berkeley)";
  1244. d354 1
  1245. a354 1
  1246.     sprintf(fmtName, "%.350s/%.100s", dirs[i].fmtDir, argv[0]);
  1247. @
  1248.  
  1249.  
  1250. 1.3
  1251. log
  1252. @Change in message wording.
  1253. @
  1254. text
  1255. @d18 1
  1256. a18 1
  1257. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.2 88/12/28 16:34:38 ouster Exp Locker: ouster $ SPRITE (Berkeley)";
  1258. d369 1
  1259. a369 1
  1260.         chmod(fmtName, 0777);
  1261. @
  1262.  
  1263.  
  1264. 1.2
  1265. log
  1266. @First version of man now appears to be useable.
  1267. @
  1268. text
  1269. @d18 1
  1270. a18 1
  1271. static char rcsid[] = "$Header: /a/newcmds/man/RCS/man.c,v 1.1 88/12/23 17:22:16 ouster Exp Locker: ouster $ SPRITE (Berkeley)";
  1272. d357 1
  1273. a357 1
  1274.         printf("Reformatting man page.  Please wait...\n");
  1275. @
  1276.  
  1277.  
  1278. 1.1
  1279. log
  1280. @Initial revision
  1281. @
  1282. text
  1283. @d18 1
  1284. a18 1
  1285. static char rcsid[] = "$Header: proto.c,v 1.2 88/03/11 08:39:08 ouster Exp $ SPRITE (Berkeley)";
  1286. d26 3
  1287. d31 8
  1288. d47 4
  1289. d53 4
  1290. d82 3
  1291. a84 1
  1292.  * Name of configuration file:
  1293. d87 6
  1294. a92 3
  1295. #ifndef CONFIG_FILE
  1296. #define CONFIG_FILE "/a/newcmds/man/config"
  1297. #endif
  1298. d265 5
  1299. a269 1
  1300.     int i;
  1301. d276 1
  1302. d284 6
  1303. a289 1
  1304.     if (ReadConfig(CONFIG_FILE, sectionName) != 0) {
  1305. d301 4
  1306. a304 3
  1307.     printf("Man will check the following directories:\n");
  1308.     for (i = 0; i < numDirs; i++) {
  1309.     printf("    %s %s\n", dirs[i].sourceDir, dirs[i].fmtDir);
  1310. d306 98
  1311. @
  1312.